package it.tcon.xbeedriver;

import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;


import com.rapplogic.xbee.api.XBee;
import com.rapplogic.xbee.api.XBeeAddress16;
import com.rapplogic.xbee.api.XBeeAddress64;
import com.rapplogic.xbee.api.XBeeException;
import com.rapplogic.xbee.api.zigbee.ZNetNodeIdentificationResponse.DeviceType;
import com.thoughtworks.xstream.*;
import com.thoughtworks.xstream.io.xml.DomDriver;
public class XBeeCom {
	
	private XBee xbee;
	private Logger logger = Logger.getLogger(XBeeCom.class);
	private String nodesFilename = "nodes.xml";
	private XBeeNodeList nodeList;
	private XBeeNode myNode;
	//XStream xs = new XStream();
	private XBeeSend send;
	
	public XBeeCom(){
		
		BasicConfigurator.configure();
		//logger.info("message");
		
		nodeList = new XBeeNodeList();

		xbee = new XBee();
		send = new XBeeSend(this.xbee);
	}
	
	
	/**
	 * Create a serial connection to the XBee device through the xbee-api by Andrew Rapp
	 * @param port The name of the serial port in witch is connected the XBee device
	 * @param baudRate The baud rate for manage the PC-XBee connection
	 * @return <tt>true</tt> if the connection is successful, <tt>false</tt> otherwise.
	 * 
	 */
	public boolean xbeeConnect(String port, int baudRate){
		
				
		try {
			this.xbee.open(port, baudRate);
			
			loadSavedNode();
			nodeList.setAllInactive();
			
			//setMyNode();
			//nodeList.addNode(myNode);
			
			return true;
		} catch (XBeeException e) {
			e.printStackTrace();
			return false;
		}
	
		/*	catch (XBeeNodeException e) {
				if(e.getMessage().compareTo("NodeAlreadyOnListException") == 0)
					return true;
				else{
			e.printStackTrace();
			return false;}
		}*/
		
	}
	
	/**
	 * Disconnect the XBee device connected to the PC saving all the needed parameter for the restart
	 * TODO return <tt>true</tt> if the disconnection is successful, <tt>false</tt> otherwise.
	 */
	public void xbeeDisconnect(){
		
	}
	/**
	 * Start the receiver for this device. The receiver manage all the packet coming to the coordinator
	 * taking care about the synchronization of the saved node.
	 * Every packet received has is own thread that check, manage, store the received message
	 */
	public void xbeeReceiver(){
		XBeeReceiver receiver = new XBeeReceiver(xbee, nodeList);
		Thread rcv = new Thread(receiver);
		rcv.start();
	}
	/**
	 * Send a command through this device (coordinator)
	 * @throws XBeeException 
	 */
	public void xbeeSend(String command,String type,XBeeAddress64 addr,XBeeAddress16 addr16) throws XBeeException{

		if(type.compareTo("AT") == 0)
			send.sendAtCommand(command);
		else if(type.compareTo("RAT")== 0)
			send.sendRemoteAtCommand(command, addr, addr16);
	}
	/**
	 * Read all the saved nodes on the nodes.dat file and set the inactive flag to all node 
	 * @throws IOException 
	 */
	private void loadSavedNode(){
		
		XStream xs = new XStream(new DomDriver());
		File fn = new File(nodesFilename);
		
		if(!fn.exists()){
			try {
				fn.createNewFile();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			return;
		}
		
		FileInputStream inStream = null;
		ObjectInputStream inObjStream = null;
		
		try {
        
        	inStream = new FileInputStream(nodesFilename);
        	inObjStream = xs.createObjectInputStream(inStream);
        	
        		while(true){
        			XBeeNode node = (XBeeNode)inObjStream.readObject();
        			if(node != null)
        				nodeList.addNode(node);
        		}
        		
        }catch(EOFException e){
        	logger.debug("End of file - Read all node on file");
        	//e.printStackTrace();
        }
        catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (XBeeNodeException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		finally{
			
			try {
				inStream.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	
	}
	
	public void saveNodeList(){
		XStream xs = new XStream();
		xs.alias("XBeeNode", XBeeNode.class);
		File file = new File(nodesFilename);
		ObjectOutputStream out = null;
		 try {
			 	if(!file.exists()){
			 		file.createNewFile();
			 	}
	            FileOutputStream fs = new FileOutputStream(file);
	            
	            XBeeNode[] nodes = nodeList.getAllNodes();
	            
	            out = xs.createObjectOutputStream(fs,"XBeeNodeList");
	            
	            for(int i = 0; i < nodes.length; i++)
	            	out.writeObject(nodes[i]);
	     
	            out.close();
	        
		 } catch (FileNotFoundException e) {
	        e.printStackTrace();
	     } catch (IOException e) {
			e.printStackTrace();
	     }
	     
	     finally{
	    	 if(out != null)
	    		try{
	    			out.close();
	    		}
	    	 	catch(IOException e){
	    	 		e.printStackTrace();
	    	 	}
	    	 
	     }

	}
	
	/**
	 * Setup my node properties(type,address, services etc).
	 */
	private void setMyNode(){
		myNode = new XBeeNode(new XBeeAddress64(0,19,162,0,64,10,66,156),DeviceType.COORDINATOR);
		this.logger.debug("Coordinator Address is:" + myNode.getAddress64());
	}
	
	/*
	private void setMyNodeAddress(){
		myNode.setAddress64(new XBeeAddress64(0,19,162,0,64,10,51,126));
	}
	*/
	
	//TODO testing method
	public String getNodes(){
		
		StringBuffer sb = new StringBuffer();
		XBeeNode[] nodes = this.nodeList.getAllNodes();
		if(nodes == null)
			return "ERROR XX_XX_XX";
		sb.append("Nodes:");
		for(int i = 0; i < nodes.length; i++){
			if(nodes[i] != null && nodes[i].getAddress64() != null)
			{
				sb.append("\nAddress ["+i+"]: "+nodes[i].getAddress64String()+" Type: "+nodes[i].getType());
			}
			//return nodes[1].getSerialNumberHigh().toString();
		
		}
		return sb.toString();
	}
	public XBeeNode[] getAllNodes(){
		return this.nodeList.getAllNodes(); 
	}
	
	public void xbeeDiscover(){
		
		try {
			this.xbeeSend("ND", "AT", null, null);
		} catch (XBeeException e) {
			e.printStackTrace();
		}
	}
	public void setService(String address,XBeeNodeService service) throws XBeeNodeException{
		if(this.nodeList.isPresent(address)){
			XBeeNode node = this.nodeList.getNode(address);
			service.setNodeCom(node,this.xbee);
			node.addService(service);
			this.nodeList.updateNode(node);
		}
		else throw new XBeeNodeException("NodeNotOnListException");
	}
	public String getService(String address, String serviceName){
		if(this.nodeList.isPresent(address)){
			XBeeNode node = this.nodeList.getNode(address);
			logger.debug("Starting the service");
			String response = node.startService(serviceName);
			if( response != null)
				logger.debug("node  service:" + response);
			else
				logger.debug("Response error");
			return node.startService(serviceName);
		}
		else
			logger.debug("node not present on list");
		return null;
	}
	public ConcurrentHashMap<String,XBeeNodeService> getServices(String address) throws XBeeNodeException{
		if(this.nodeList.isPresent(address)){
			XBeeNode node = this.nodeList.getNode(address);
			ConcurrentHashMap<String,XBeeNodeService> nodeServices = node.getServices();
			return nodeServices;
		}
		else{
			throw new XBeeNodeException("NodeNotOnListException");
		}
	}
	
	
	
}
